#!/bin/bash
### BEGIN INIT INFO
# Provides:          @DAEMON@
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Required-Start:
# Required-Stop:
# Short-Description: Yandex daemon
### END INIT INFO

USER=metrika
GROUP=metrika
SHELL=/bin/bash
PROGRAM=@DAEMON@
SYSCONFDIR=/etc/$PROGRAM
LOGDIR=/var/log/$PROGRAM
LOCALSTATEDIR=/var/lock
BINDIR=/usr/bin
CRONFILE=/etc/cron.d/@CRONFILE@
CNFFILE=$SYSCONFDIR/config.xml
LOCKFILE=$LOCALSTATEDIR/$PROGRAM
RETVAL=0

DEFAULT_NUMBER_OF_PROCESSES=1
NUMBER_OF_PROCESSES=$DEFAULT_NUMBER_OF_PROCESSES

LOG_FILE=""

# On x86_64, check for required instruction set.
if uname -mpi | grep -q 'x86_64'; then
	if ! grep -q 'sse4_2' /proc/cpuinfo; then
		# On KVM, cpuinfo could falsely not report SSE 4.2 support, so skip the check.
		if ! grep -q 'Common KVM processor' /proc/cpuinfo; then

		    # Some other VMs also report wrong flags in cpuinfo.
			# Tricky way to test for instruction set:
			#  create temporary binary and run it;
			#  if it get caught illegal instruction signal,
			#  then required instruction set is not supported really.
			#
			# Generated this way:
			# gcc -xc -Os -static -nostdlib - <<< 'void _start() { __asm__("pcmpgtq %%xmm0, %%xmm1; mov $0x3c, %%rax; xor %%rdi, %%rdi; syscall":::"memory"); }' && strip -R .note.gnu.build-id -R .comment -R .eh_frame -s ./a.out && gzip -c -9 ./a.out | base64 -w0; echo

			if ! (echo -n 'H4sICAwAW1cCA2Eub3V0AKt39XFjYmRkgAEmBjsGEI+H0QHMd4CKGyCUAMUsGJiBJDNQNUiYlQEZOKDQclB9cnD9CmCSBYqJBRxQOvBpSQobGfqIAWn8FuYnPI4fsAGyPQz/87MeZtArziguKSpJTGLQK0mtKGGgGHADMSgoYH6AhTMPNHyE0NQzYuEzYzEXFr6CBPQDANAsXKTwAQAA' | base64 -d | gzip -d > /tmp/clickhouse_test_sse42 && chmod a+x /tmp/clickhouse_test_sse42 && /tmp/clickhouse_test_sse42); then
				echo 'SSE 4.2 instruction set is not supported'
				exit 3
			fi
		fi
	fi
fi

# С помощью xmlstarlet пытаемся взять некоторые параметры из конфига
if command -v xmlstarlet >/dev/null 2>&1; then
	NUMBER_OF_PROCESSES=$(xmlstarlet sel -t -v "/yandex/number_of_processes" $CNFFILE || echo $DEFAULT_NUMBER_OF_PROCESSES)
	LOG_FILE=$(xmlstarlet sel -t -v "/yandex/logger/log" $CNFFILE || echo $LOG_FILE)
fi

PIDDIR=/var/run/$PROGRAM
PIDFILE_PREFIX=$PIDDIR/$PROGRAM
PIDFILE_RE="$PIDFILE_PREFIX[0-9]*.pid"

SUPPORTED_COMMANDS="{start|stop|status|restart|forcestop|forcerestart|reload|condstart|condstop|condrestart|condreload}"
is_supported_command()
{
	echo $SUPPORTED_COMMANDS | grep -E "(\{|\|)$1(\||})" &> /dev/null
}

generate_program_name()
{
	if [ $NUMBER_OF_PROCESSES -eq 1 ]; then
		echo $PROGRAM
	else
		echo $PROGRAM$1
	fi
}

generate_pid_name()
{
	if [ $NUMBER_OF_PROCESSES -gt 1 ]; then
		echo $PIDFILE_PREFIX$1.pid
	else
		echo $PIDFILE_PREFIX.pid
	fi
}

specific_log_file_for_each_process()
{
	# Не будем менять имя лог файла, при одном процессе
	# Не выставляем, если имя лог файла пустое
	if [ $NUMBER_OF_PROCESSES -gt 1 ] && [ "$LOG_FILE" != "" ]; then
		log_file=$(echo $LOG_FILE | sed "s/\.log/$1.log/")
		echo "--log-file=\"$log_file\""
	fi

	echo ""
}

find_pid_files()
{
	[[ -e $PIDDIR ]] && find $PIDDIR -regex "$PIDFILE_RE"
}

is_running()
{
	pidfile=$1

	[ -r "$pidfile" ] && pgrep -s $(cat "$pidfile") 1> /dev/null 2> /dev/null
}

running_processes()
{
	pidfiles=$(find_pid_files)
	running=0
	for pidfile in $pidfiles; do
		if is_running $pidfile; then
			running=$(($running + 1))
		fi
	done
	echo $running
}

any_runs()
{
	if [[ $(running_processes) -gt  0 ]]; then return 0; else return 1; fi
}

all_runs()
{
	[[ $(running_processes) -eq $NUMBER_OF_PROCESSES ]]
}

wait4done()
{
	while any_runs; do
		sleep 1
	done
}

start()
{
	[ -x $BINDIR/$PROGRAM ] || exit 0
	local EXIT_STATUS
	EXIT_STATUS=0

	echo -n "Start $PROGRAM service: "
	ulimit -n 262144

	if all_runs; then
		echo -n "already running "
		EXIT_STATUS=1
	else
		mkdir -p $LOGDIR
		mkdir -p $PIDDIR
		chown -R $USER:$GROUP $LOGDIR
		chown -R $USER:$GROUP $PIDDIR
		chown -R $USER:$GROUP $SYSCONFDIR

		for i in $(seq 1 $NUMBER_OF_PROCESSES); do
			if ! is_running $(generate_pid_name $i); then
				rm -f $(generate_pid_name $i)
				# чтобы лок не удерживался в течении времени жизни дочернего процесса, освободим лок
				su -l $USER -s $SHELL -c "flock -u 9; exec -a $(generate_program_name $i) \"$BINDIR/$PROGRAM\" --daemon --pid-file=\"$(generate_pid_name $i)\" --config-file=\"$CNFFILE\" $(specific_log_file_for_each_process $i)"
				EXIT_STATUS=$?
				if [[ $EXIT_STATUS -ne 0 ]]; then
					break
				fi
			fi
		done
	fi

	if [[ $EXIT_STATUS -eq 0 ]]; then
		echo "DONE"
	else
		echo "FAILED"
	fi

	return $EXIT_STATUS
}

stop()
{
	local EXIT_STATUS
	EXIT_STATUS=0

	echo -n "Stop $PROGRAM service: "

	for pid_file in $(find_pid_files); do
		kill -TERM `cat "$pid_file"`
	done

	wait4done

	echo "DONE"
	return $EXIT_STATUS
}

restart()
{
	stop
	start
}

forcestop()
{
	local EXIT_STATUS
	EXIT_STATUS=0

	echo -n "Stop $PROGRAM service: "

	for pid_file in $(find_pid_files); do
		kill -9 `cat "$pid_file"`
	done

	wait4done

	echo "DONE"
	return $EXIT_STATUS
}

forcerestart()
{
	forcestop
	start
}

enable_cron()
{
	sed -i 's/^#*//' "$CRONFILE"
}

disable_cron()
{
	sed -i 's/^#*/#/' "$CRONFILE"
}

is_cron_disabled()
{
	[[ `grep -E "^#.*" $CRONFILE` == `cat $CRONFILE` ]];
}

main()
{
	# See how we were called.
	EXIT_STATUS=0
	case "$1" in
	start)
		start && enable_cron
		;;
	stop)
		disable_cron && stop
		;;
	restart)
		restart && enable_cron
		;;
	forcestop)
		disable_cron && forcestop
		;;
	forcerestart)
		forcerestart && enable_cron
		;;
	reload)
		restart
		;;
	condstart)
		all_runs || start
		;;
	condstop)
		any_runs && stop
		;;
	condrestart)
		any_runs && restart
		;;
	condreload)
		any_runs && restart
		;;
	esac

	exit $EXIT_STATUS
}

status()
{
	if [[ $(running_processes) -eq $NUMBER_OF_PROCESSES ]]; then
		echo "$PROGRAM service is running"
	else
		if is_cron_disabled; then
			echo "$PROGRAM service is stopped";
		else
			echo "$PROGRAM: $(($NUMBER_OF_PROCESSES - $(running_processes))) of $NUMBER_OF_PROCESSES processes unexpectedly terminated"
		fi
	fi
}

# выполняем команды, не нуждающиеся в блокировке
if ! is_supported_command "$1"; then
	echo "Usage: ${0##*/} $SUPPORTED_COMMANDS"
	exit 2
fi

if [[ "$1" == "status" ]]; then
	status
	exit 0
fi

(
	if flock -n 9; then
		main "$@"
	else
		echo "Init script is already running" && exit 1
	fi
) 9> $LOCKFILE
